//============================================================================
// Name        : exception_backtrace.cpp
// Author      :
// Version     :
// Copyright   : copy from http://kb.cnblogs.com/page/79171/
// Description : exception plus backtrace
// compile cmd:          g++ -rdynamic -g -O2 -Wall -I . exception_backtrace.cpp -o exception_backtrace
// address to line cmd:  addr2line 0x8049c79 -e ./exception_backtrace -f
//__attribute__ ((noinline))
//============================================================================

#include <exception>
#include <string>
#include <execinfo.h>
#include <stdlib.h>
#include <cxxabi.h>
#include <iostream>
#include <sstream>
using namespace std;

//definition
#define MY_THROW(ExClass, args...) \
		do \
		{ \
			ExClass e(args); \
			e.Init(__FILE__, __PRETTY_FUNCTION__, __LINE__); \
			throw e; \
		} \
		while (false)
#define MY_DEFINE_EXCEPTION(ExClass, Base) \
		ExClass(const std::string& msg = "") throw() \
		: Base(msg) \
		  {} \
		  \
		  ~ExClass() throw() {} \
		  \
/* override */ std::string GetClassName() const \
		{ \
			  return #ExClass; \
		}
class ExceptionBase : public std::exception
{
public:
	ExceptionBase(const std::string& msg = "") throw();
	virtual ~ExceptionBase() throw();
	void Init(const char* file, const char* func, int line);
	virtual std::string GetClassName() const;
	virtual std::string GetMessage() const;
	const char* what() const throw();
	const std::string& ToString() const;
	std::string GetStackTrace() const;
protected:
	std::string mMsg;
	const char* mFile;
	const char* mFunc;
	int mLine;
private:
	enum { MAX_STACK_TRACE_SIZE = 50 };
	void* mStackTrace[MAX_STACK_TRACE_SIZE];
	size_t mStackTraceSize;
	mutable std::string mWhat;
};
class ExceptionDerived : public ExceptionBase
{
public:
	MY_DEFINE_EXCEPTION(ExceptionDerived, ExceptionBase);
};


ExceptionBase::ExceptionBase(const std::string& msg) throw()
    		: mMsg(msg),
    		  mFile("<unknown file>"),
    		  mFunc("<unknown func>"),
    		  mLine(-1),
    		  mStackTraceSize(0)
{}
ExceptionBase::~ExceptionBase() throw()
		{}
void ExceptionBase::Init(const char* file, const char* func, int line)
{
	mFile = file;
	mFunc = func;
	mLine = line;
	mStackTraceSize = backtrace(mStackTrace, MAX_STACK_TRACE_SIZE);
}
std::string ExceptionBase::GetClassName() const
{
	return "ExceptionBase";
}
const char* ExceptionBase::what() const throw()
		{
	return ToString().c_str();
		}
const std::string& ExceptionBase::ToString() const
{
	if (mWhat.empty())
	{
		stringstream sstr("");
		if (mLine > 0)
		{
			sstr << mFile << "(" << mLine << ")";
		}
		sstr <<  ": " << GetClassName();
		if (!GetMessage().empty())
		{
			sstr << ": " << GetMessage();
		}
		sstr << "\nStack Trace:\n";
		sstr << GetStackTrace();
		mWhat = sstr.str();
	}
	return mWhat;
}
std::string ExceptionBase::GetMessage() const
{
	return mMsg;
}
std::string ExceptionBase::GetStackTrace() const
{
	if (mStackTraceSize == 0)
		return "<No stack trace>\n";
	char** strings = backtrace_symbols(mStackTrace, 10);
	std::string result;
	for(size_t i = 0; i < mStackTraceSize && strings; i++)
	{
		result += strings[i];
		result += "\n";
	}
	return result;
}
/*
 * test-main
 */
int f2()
{
	MY_THROW(ExceptionDerived, "f2 throw");
}
void f1()
{
	try
	{
		f2();
	}
	catch (ExceptionDerived& e)
	{
		cout << e.what() << endl;
	}
}
int f3()
{
	f1();
}
int main()
{
	f3();
}
